#include "dev_ps2.h"

typedef struct
{
	uint8_t  ps2_data_update;
	uint16_t key_butturn_state;
	uint16_t key_butturn_last_state;
	uint8_t  rocker_rx;
	uint8_t  rocker_ry;
	uint8_t  rocker_lx;
	uint8_t  rocker_ly;
	
	Ps2_State_Cb Cb;
}ps2_private_t;

typedef struct{
	ps2_private_t priv;
}dev_ps2_t;

	
ps2_private_t dev_ps2;

#define DELAY_TIME  delay_us(5); 

u8 Comd[2]={0x01,0x42};	//开始命令。请求数据
uint8_t Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //数据存储缓冲区
//按键值与按键明
uint16_t MASK[]={
    PSB_SELECT,
    PSB_L3,
    PSB_R3 ,
    PSB_START,
    PSB_PAD_UP,
    PSB_PAD_RIGHT,
    PSB_PAD_DOWN,
    PSB_PAD_LEFT,
    PSB_L2,
    PSB_R2,
    PSB_L1,
    PSB_R1 ,
    PSB_GREEN,
    PSB_RED,
    PSB_BLUE,
    PSB_PINK
	};	

	
	
/****************************************************************************私有函数************************************************************************************/
	/**
* @brief  绝对延时
	* @parm   None
	* @retval None
	*/
void __delay_us(uint32_t nus)
{
	for(uint32_t i = 0;i <nus;i++)
	{
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
		__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	}	
}
	/**
* @brief  延时
	* @parm   None
	* @retval None
	*/
static void delay_us(uint32_t nus)
{
	__delay_us(nus);
}
	/**
	* @brief  单片机向PS2写命令
	* @parm   cmd
	* @retval None
	*/
static void Dev_Ps2_Cmd(uint8_t cmd)
{
	volatile uint16_t ref=0x01;
	Data[1] = 0;
	for(ref=0x01;ref<0x0100;ref<<=1)
	{
		if(ref&cmd)
		{
			PS2_CMD_H;                   //输出一位控制位
		}
		else PS2_CMD_L;

		PS2_CLK_H;                        //时钟拉高
		delay_us(5);
		PS2_CLK_L;
		delay_us(5);
		PS2_CLK_H;
		if(PS2_DAT_R)
			Data[1] = ref|Data[1];
	}
	delay_us(16);
}

//判断是否为红灯模式,0x41=模拟绿灯，0x73=模拟红灯
//返回值；0，红灯模式
//		  其他，其他模式
uint8_t Dev_PS2_RedLight(void)
{
	PS2_CS_L;
	Dev_Ps2_Cmd(Comd[0]);  //开始命令
	Dev_Ps2_Cmd(Comd[1]);  //请求数据
	PS2_CS_H;
	if( Data[1] == 0X73)   return 0 ;
	else return 1;

}

/**
	* @brief  单片机对PS2读数据
	* @parm   None
	* @retval None
	*/
static void Dev_Ps2_Read(void)
{
	volatile uint8_t byte = 0;//必须要用volatile关键词来定义。关键词的作用请自行百度。
	uint16_t i = 0x01;
	
	PS2_CS_L;						//CS拉低
	Dev_Ps2_Cmd(Comd[0]);			//开始命令
	Dev_Ps2_Cmd(Comd[1]);			//请求数据
	for(byte=2;byte<9;byte++)
	{
		for(i=0x01;i<0x100;i<<=1)
		{
			PS2_CLK_H;			//单片机发送高电平，告诉接收器要准备数据了		
			delay_us(5);		//接收器准备数据的时间
			PS2_CLK_L;			//发送低电平，告诉接收器 单片机要开始读数据了
			delay_us(5);	
			PS2_CLK_H;			
			if(PS2_DAT_R)
			{
				Data[byte] = i| Data[byte];
			}
		}
		delay_us(5);
	}
	PS2_CS_H;					//CS拉高
}

/**
	* @brief  数组清空函数
	* @parm   None
	* @retval None
	*/
static void Dev_Ps2_DataClear(void)
{
	u8 i;
	for(i=0;i<9;i++)
	{
		Data[i]=0x00;
	}
}

/**
	* @brief  数组清空函数
	* @parm   None
	* @retval None
	*/
static void Dev_PS2_ShortPoll(void)
{
	PS2_CS_L;
	delay_us(16);
	Dev_Ps2_Cmd(0x01);  
	Dev_Ps2_Cmd(0x42);  
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0x00);
	Dev_Ps2_Cmd(0x00);
	PS2_CS_H;
	delay_us(16);	
}

/**
	* @brief  进入配置
	* @parm   None
	* @retval None
	*/
static void Dev_PS2_EnterConfing(void)
{
    PS2_CS_L;
	delay_us(16);
	Dev_Ps2_Cmd(0x01);  
	Dev_Ps2_Cmd(0x43);  
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0x01);
	Dev_Ps2_Cmd(0x00);
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0X00);
	PS2_CS_H;
	delay_us(16);
}

/**
	* @brief  发送模式设置
	* @parm   None
	* @retval None
	*/
static void Dev_PS2_TurnOnAnalogMode(void)
{
	PS2_CS_L;
	Dev_Ps2_Cmd(0x01);  
	Dev_Ps2_Cmd(0x44);  
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0x01); //analog=0x01;digital=0x00  软件设置发送模式
	Dev_Ps2_Cmd(0xEE); //Ox03锁存设置，即不可通过按键“MODE”设置模式。
				   //0xEE不锁存软件设置，可通过按键“MODE”设置模式。
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0X00);
	PS2_CS_H;
	delay_us(16);
}

/**
	* @brief  振动设置
	* @parm   None
	* @retval None
	*/
static void Dev_PS2_VibrationMode(void)
{
	PS2_CS_L;
	delay_us(16);
	Dev_Ps2_Cmd(0x01);  
	Dev_Ps2_Cmd(0x4D);  
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0x00);
	Dev_Ps2_Cmd(0X01);
	PS2_CS_H;
	delay_us(16);	
}

/**
	* @brief  完成并保存配置
	* @parm   None
	* @retval None
	*/
static void Dev_PS2_ExitConfing(void)
{
    PS2_CS_L;
	delay_us(16);
	Dev_Ps2_Cmd(0x01);  
	Dev_Ps2_Cmd(0x43);  
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0x00);
	Dev_Ps2_Cmd(0x5A);
	Dev_Ps2_Cmd(0x5A);
	Dev_Ps2_Cmd(0x5A);
	Dev_Ps2_Cmd(0x5A);
	Dev_Ps2_Cmd(0x5A);
	PS2_CS_H;
	delay_us(16);
}

/**
	* @brief  手柄配置初始化
	* @parm   None
	* @retval None
	*/
static void Dev_PS2_SetInit(void)
{
	Dev_PS2_ShortPoll();
	Dev_PS2_ShortPoll();
	Dev_PS2_ShortPoll();
	Dev_PS2_EnterConfing();		//进入配置模式
	Dev_PS2_TurnOnAnalogMode();	//“红绿灯”配置模式，并选择是否保存
	Dev_PS2_VibrationMode();	//开启震动模式
	Dev_PS2_ExitConfing();		//完成并保存配置
}

/**
	* @brief  用来读出按键值的函数
	* @parm   None
	* @retval 成功则返回index+1，失败则返回0。
	*/
static uint8_t Dev_Ps2_DataKey(void)
{
	uint8_t index;

	Dev_Ps2_DataClear();						//清空数组
	Dev_Ps2_Read();									//单片机读接收器的数据
	
	dev_ps2.key_butturn_state=(Data[4]<<8)|Data[3];//根据数据意义对照表，定义一个16位的变量。

	for(index=0;index<16;index++)//当我们按下遥控器的按键时，数据会传到Data[3]或者Data[4]来。我这里进行16次for循环，用来判断哪个按键按下了。
	{//例如：当按下了SELECT按键，Data[3]=11111110B。Handkey=1111 1111 1111 1110B。
		if((dev_ps2.key_butturn_state & (1<<(MASK[index]-1)))==0)	//当第一次进入循环，Handkey&(1<<(MASK[0]-1)))
		{
			return index+1;//-->Handkey&(1<<0)--->1111 1111 1111 1110B & 0000 0000 0000 0001=0																								          													
		}
	}
	
	if(Data[1] == 0x73) /*红光模式 摇杆有效*/
	{
		dev_ps2.rocker_rx= Data[5];//根据数据意义对照表，定义一个8位的变量。
		dev_ps2.rocker_ry= Data[6];//根据数据意义对照表，定义一个8位的变量。
		dev_ps2.rocker_lx= Data[7];//根据数据意义对照表，定义一个8位的变量。
		dev_ps2.rocker_ly= Data[8];//根据数据意义对照表，定义一个8位的变量。
	}
	else
	{	dev_ps2.rocker_rx= 128;//根据数据意义对照表，复位值中间值127。
		dev_ps2.rocker_ry= 128;//根据数据意义对照表，复位值中间值127。
		dev_ps2.rocker_lx= 128;//根据数据意义对照表，复位值中间值127。
		dev_ps2.rocker_ly= 128;//根据数据意义对照表，复位值中间值127。
	}
	
	return  0;
}

/**
* @brief  PS2手柄任务
	* @parm   None
	* @retval None
	*/
static void Dev_Ps2_Task(void *pvParameter)
{
	while(1)
	{ 
		uint8_t key=Dev_Ps2_DataKey(); 
		if(key!=0)                   //有按键按下
    	{
			if(dev_ps2.key_butturn_state != dev_ps2.key_butturn_last_state)
			{
				dev_ps2.key_butturn_last_state = dev_ps2.key_butturn_state;
				if(dev_ps2.Cb != NULL)
				{
					for(uint8_t index=0;index<16;index++)//当我们按下遥控器的按键时，数据会传到Data[3]或者Data[4]来。我这里进行16次for循环，用来判断哪个按键按下了。
					{
						if((dev_ps2.key_butturn_state & (1<<(MASK[index]-1)))==0)	//当第一次进入循环，Handkey&(1<<(MASK[0]-1)))
						{
							dev_ps2.Cb(index+1,1);
						}
						else
						{
							dev_ps2.Cb(index+1,0);
						}
					}
				}
			}
    	}
		else
		{
			dev_ps2.key_butturn_state  = 0;
			dev_ps2.key_butturn_last_state = 0;
		}
				
		vTaskDelay(50);
	}
}


/****************************************************************************接口函数************************************************************************************/

/**
* @brief  手柄震动函数，
	* @parm   motor1:右侧小震动电机 0x00关，其他开
	   motor2:左侧大震动电机 0x40~0xFF 电机开，值越大 震动越大
	* @retval None
	*/
void Dev_PS2_Vibration(u8 motor1, u8 motor2)
{
	PS2_CS_L;
	delay_us(16);
    Dev_Ps2_Cmd(0x01);  //开始命令
	Dev_Ps2_Cmd(0x42);  //请求数据
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(motor1);
	Dev_Ps2_Cmd(motor2);
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0X00);
	Dev_Ps2_Cmd(0X00);
	PS2_CS_H;
	delay_us(16);  
}

/**
* @brief  PS2手柄摇杆RX数据
	* @parm   None
	* @retval None
	*/
uint8_t Dev_Ps2_Rocker_Rx_Get(void)
{
	return dev_ps2.rocker_rx;
}
/**
* @brief  PS2手柄摇杆RY数据
	* @parm   None
	* @retval None
	*/
uint8_t Dev_Ps2_Rocker_Ry_Get(void)
{
	return dev_ps2.rocker_ry;
}
/**
* @brief  PS2手柄摇杆LX数据
	* @parm   None
	* @retval None
	*/
uint8_t Dev_Ps2_Rocker_Lx_Get(void)
{
	return dev_ps2.rocker_lx;
}
/**
* @brief  PS2手柄摇杆LY数据
	* @parm   None
	* @retval None
	*/
uint8_t Dev_Ps2_Rocker_Ly_Get(void)
{
	return dev_ps2.rocker_ly;
}
/**
* @brief  PS2手柄是否有新数据
	* @parm   None
	* @retval None
	*/
uint8_t Dev_Ps2_data_update_Get(void)
{
	return dev_ps2.ps2_data_update;
}
/**
* @brief  PS2手柄数据回调 ID 值
	* @parm   None
	* @retval None
	*/
void Dev_Ps2_Cb_Register(Ps2_State_Cb Cb)
{
	dev_ps2.Cb = Cb;
}
/**
* @brief  PS2手柄组件初始化
	* @parm   None
	* @retval None
	*/
void Dev_Ps2_Init()
{	
	Dev_PS2_SetInit();
	
	xTaskCreate(Dev_Ps2_Task, "Dev_Ps2_Task", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+4, NULL);
}
